!>\file aerinterp.F90
!! This file contains subroutines of reading and interpolating
!! aerosol data for MG microphysics.

!>\ingroup mod_GFS_phys_time_vary
!! This module contain subroutines of reading and interpolating
!! aerosol data for MG microphysics.
module aerinterp

    implicit none

    private read_netfaer

    public :: read_aerdata, setindxaer, aerinterpol,read_aerdataf

contains

      SUBROUTINE read_aerdata (me, master, iflip, idate, errmsg, errflg)
      use machine, only: kind_phys, kind_io4, kind_io8
      use aerclm_def
      use netcdf

!--- in/out
      integer, intent(in) :: me, master, iflip, idate(4)
      character(len=*), intent(inout) :: errmsg
      integer, intent(inout) :: errflg

!--- locals
      integer      :: ncid, varid, ndims, dim1, dim2, dim3, hmx
      integer      :: i, j, k, n, ii, imon, klev
      character    :: fname*50, mn*2, vname*10
      logical      :: file_exist

      integer, allocatable  :: invardims(:)
!
!! ===================================================================
      if (me == master) then
         if ( iflip == 0 )  then             ! data from toa to sfc
          print *, "GFS is top-down"
         else
          print *, "GFS is bottom-up"
         endif
      endif
!
!! ===================================================================
!! check if all necessary files exist
!! ===================================================================
      do imon = 1, 12
         write(mn,'(i2.2)') imon
         fname=trim("aeroclim.m"//mn//".nc")
         inquire (file = fname, exist = file_exist)
         if (.not. file_exist) then
            errmsg = 'Error in read_aerdata: file ' // trim(fname) // ' not found'
            errflg = 1
            return
         endif
      enddo
!
!! ===================================================================
!! fetch dim spec and lat/lon from m01 data set
!! ===================================================================
      fname=trim("aeroclim.m"//'01'//".nc")
      call nf_open(fname , nf90_NOWRITE, ncid)

      vname =  trim(specname(1))
      call nf_inq_varid(ncid, vname, varid)
      call nf_inq_varndims(ncid, varid, ndims)

      if(.not. allocated(invardims)) allocate(invardims(3))
      call nf_inq_vardimid(ncid,varid,invardims)
      call nf_inq_dimlen(ncid, invardims(1), dim1)
      call nf_inq_dimlen(ncid, invardims(2), dim2)
      call nf_inq_dimlen(ncid, invardims(3), dim3)

! specify latsaer, lonsaer, hmx
      lonsaer = dim1
      latsaer = dim2
      levsw = dim3

      if(me==master) then
         print *, 'MERRA2 dim: ',dim1, dim2, dim3
      endif

! allocate arrays

      if (.not. allocated(aer_lat)) then
        allocate(aer_lat(latsaer))
        allocate(aer_lon(lonsaer))
      endif

! construct lat/lon array
      call nf_inq_varid(ncid, 'lat', varid)
      call nf_get_var(ncid, varid, aer_lat)
      call nf_inq_varid(ncid, 'lon', varid)
      call nf_get_var(ncid, varid, aer_lon)
      call nf_close(ncid)
      END SUBROUTINE read_aerdata
!
!**********************************************************************
      SUBROUTINE read_aerdataf ( me, master, iflip, idate, FHOUR, errmsg, errflg)
      use machine, only: kind_phys, kind_io4, kind_io8
      use aerclm_def

!--- in/out
      integer, intent(in) :: me, master, iflip, idate(4)
      character(len=*), intent(inout) :: errmsg
      integer, intent(inout) :: errflg
      real(kind=kind_phys), intent(in) :: fhour
!--- locals
      integer      :: i, j, k, n, ii, imon, klev, n1, n2
      logical      :: file_exist
      integer  IDAT(8),JDAT(8)
      real(kind=kind_phys) rjday
      real(8) RINC(5)
      integer jdow, jdoy, jday
      real(4) rinc4(5)
      integer w3kindreal,w3kindint      

      integer, allocatable  :: invardims(:)
!
      if (.not. allocated(aerin)) then
        allocate(aerin(iamin:iamax,jamin:jamax,levsaer,ntrcaerm,timeaer))
        allocate(aer_pres(iamin:iamax,jamin:jamax,levsaer,timeaer))
      endif

! allocate local working arrays
!!  found interpolation months
      IDAT = 0
      IDAT(1) = IDATE(4)
      IDAT(2) = IDATE(2)
      IDAT(3) = IDATE(3)
      IDAT(5) = IDATE(1)
      RINC = 0.
      RINC(2) = FHOUR
      call w3kind(w3kindreal,w3kindint)
      if(w3kindreal == 4) then
        rinc4 = rinc
        CALL W3MOVDAT(RINC4,IDAT,JDAT)
      else
        CALL W3MOVDAT(RINC,IDAT,JDAT)
      endif
!
      jdow = 0
      jdoy = 0
      jday = 0
      call w3doxdat(jdat,jdow,jdoy,jday)
      rjday = jdoy + jdat(5) / 24.
      IF (RJDAY < aer_time(1)) RJDAY = RJDAY+365.
!
      n2 = 13
      do j=2, 12
       if (rjday < aer_time(j)) then
          n2 = j
          exit
       endif
      enddo
      n1 = n2 - 1
      if (n2 > 12) n2 = n2 -12
!! ===================================================================
      call read_netfaer(n1, iflip, 1)
      call read_netfaer(n2, iflip, 2)
!! ===================================================================
      n1sv=n1
      n2sv=n2
!---
      END SUBROUTINE read_aerdataf
!
      SUBROUTINE setindxaer(npts,dlat,jindx1,jindx2,ddy,dlon,           &
                            iindx1,iindx2,ddx,me,master)
!
      USE MACHINE,  ONLY: kind_phys
      use aerclm_def, only: aer_lat, jaero=>latsaer,                    &
                            aer_lon, iaero=>lonsaer
!
      implicit none
!
      integer me, master
      integer npts, JINDX1(npts),JINDX2(npts),IINDX1(npts),IINDX2(npts)
      real(kind=kind_phys) dlat(npts),DDY(npts),dlon(npts),DDX(npts)
!
      integer i,j

      DO J=1,npts
        jindx2(j) = jaero + 1
        do i=1,jaero
          if (dlat(j) < aer_lat(i)) then
            jindx2(j) = i
            exit
          endif
        enddo
        jindx1(j) = max(jindx2(j)-1,1)
        jindx2(j) = min(jindx2(j),jaero)
        if (jindx2(j) .ne. jindx1(j)) then
          DDY(j) = (dlat(j)            - aer_lat(jindx1(j))) &
                 / (aer_lat(jindx2(j)) - aer_lat(jindx1(j)))
        else
          ddy(j) = 1.0
        endif

      ENDDO

      DO J=1,npts
        iindx2(j) = iaero + 1
        do i=1,iaero
          if (dlon(j) < aer_lon(i)) then
            iindx2(j) = i
            exit
          endif
        enddo
        iindx1(j) = max(iindx2(j)-1,1)
        iindx2(j) = min(iindx2(j),iaero)
        if (iindx2(j) .ne. iindx1(j)) then
          ddx(j) = (dlon(j)            - aer_lon(iindx1(j))) &
                 / (aer_lon(iindx2(j)) - aer_lon(iindx1(j)))
        else
          ddx(j) = 1.0
        endif
      ENDDO

      RETURN
      END SUBROUTINE setindxaer
!
!**********************************************************************
!**********************************************************************
!
      SUBROUTINE aerinterpol( me,master,nthrds,npts,IDATE,FHOUR,iflip, jindx1,jindx2, &
                             ddy,iindx1,iindx2,ddx,lev,prsl,aerout)
!
      use machine, only: kind_phys, kind_io4, kind_io8
      use aerclm_def

      implicit none
      integer, intent(in) :: iflip
      integer   i1,i2, iday,j,j1,j2,l,npts,nc,n1,n2,lev,k,i,ii, klev
      real(kind=kind_phys) fhour,temj, tx1, tx2,temi, tem
      real(kind=kind_phys), dimension(npts) :: temij,temiy,temjx,ddxy
      
!

      integer  JINDX1(npts), JINDX2(npts), iINDX1(npts), iINDX2(npts)
      integer  me,idate(4), master, nthrds
      integer  IDAT(8),JDAT(8)
!
      real(kind=kind_phys) DDY(npts), ddx(npts),ttt
      real(kind=kind_phys) aerout(npts,lev,ntrcaer)
      real(kind=kind_phys) aerpm(npts,levsaer,ntrcaer)
      real(kind=kind_phys) prsl(npts,lev), aerpres(npts,levsaer)
      real(kind=kind_phys) rjday
      integer jdow, jdoy, jday
      real(8) RINC(5)
      real(4) rinc4(5)
      integer w3kindreal,w3kindint

!
      IDAT = 0
      IDAT(1) = IDATE(4)
      IDAT(2) = IDATE(2)
      IDAT(3) = IDATE(3)
      IDAT(5) = IDATE(1)
      RINC = 0.
      RINC(2) = FHOUR
      call w3kind(w3kindreal,w3kindint)
      if(w3kindreal == 4) then
        rinc4 = rinc
        CALL W3MOVDAT(RINC4,IDAT,JDAT)
      else
        CALL W3MOVDAT(RINC,IDAT,JDAT)
      endif
!
      jdow = 0
      jdoy = 0
      jday = 0
      call w3doxdat(jdat,jdow,jdoy,jday)
      rjday = jdoy + jdat(5) / 24.
      IF (RJDAY < aer_time(1)) RJDAY = RJDAY+365.
!
      n2 = 13
      do j=2, 12
       if (rjday < aer_time(j)) then
          n2 = j
          exit
       endif
      enddo
      n1 = n2 - 1
      if (n2 > 12) n2 = n2 -12
!     need to read a new month 
      if (n1.ne.n1sv) then
#ifdef DEBUG
        if (me == master) write(*,*)"read in a new month MERRA2", n2
#endif
        DO ii = 1, ntrcaerm
          do j = jamin, jamax
            do k = 1, levsaer
              do i = iamin, iamax
                aerin(i,j,k,ii,1) = aerin(i,j,k,ii,2)
              enddo   !i-loop (lon)
            enddo     !k-loop (lev)
          enddo       !j-loop (lat)
        ENDDO         ! ii-loop (ntracaerm)
!! ===================================================================
        call read_netfaer(n2, iflip, 2)
        n1sv=n1
        n2sv=n2
      end if
!
      tx1 = (aer_time(n2) - rjday) / (aer_time(n2) - aer_time(n1))
      tx2 = 1.0 - tx1
      if (n2 > 12) n2 = n2 -12

      do j=1,npts
         TEMJ     = 1.0 - DDY(J)
         TEMI     = 1.0 - DDX(J)
         temij(j) = TEMI*TEMJ
         temiy(j) = TEMI*DDY(j)
         temjx(j) = TEMJ*DDX(j)
         ddxy(j)  = DDX(j)*DDY(J)
      enddo

#ifndef __GFORTRAN__
!$OMP parallel num_threads(nthrds) default(none)             &
!$OMP          shared(npts,ntrcaer,aerin,aer_pres,prsl)      &
!$OMP          shared(ddx,ddy,jindx1,jindx2,iindx1,iindx2)   &
!$OMP          shared(aerpm,aerpres,aerout,lev,nthrds) &
!$OMP          shared(temij,temiy,temjx,ddxy)                &
!$OMP          private(l,j,k,ii,i1,i2,j1,j2,tem)             &
!$OMP          copyin(tx1,tx2) firstprivate(tx1,tx2)

!$OMP do
#endif
      DO L=1,levsaer
        DO J=1,npts
          J1    = JINDX1(J)
          J2    = JINDX2(J)
          I1    = IINDX1(J)
          I2    = IINDX2(J)
          DO ii=1,ntrcaer
           aerpm(j,L,ii) =                                                  &
           tx1*(TEMIJ(j)*aerin(I1,J1,L,ii,1)+DDXY(j)*aerin(I2,J2,L,ii,1)  &
               +TEMIY(j)*aerin(I1,J2,L,ii,1)+temjx(j)*aerin(I2,J1,L,ii,1))&
          +tx2*(TEMIJ(j)*aerin(I1,J1,L,ii,2)+DDXY(j)*aerin(I2,J2,L,ii,2)  &
               +TEMIY(j)*aerin(I1,J2,L,ii,2)+temjx(j)*aerin(I2,J1,L,ii,2))
          ENDDO

          aerpres(j,L) =                                                    &
           tx1*(TEMIJ(j)*aer_pres(I1,J1,L,1)+DDXY(j)*aer_pres(I2,J2,L,1)  &
               +TEMIY(j)*aer_pres(I1,J2,L,1)+temjx(j)*aer_pres(I2,J1,L,1))&
          +tx2*(TEMIJ(j)*aer_pres(I1,J1,L,2)+DDXY(j)*aer_pres(I2,J2,L,2)  &
               +TEMIY(j)*aer_pres(I1,J2,L,2)+temjx(j)*aer_pres(I2,J1,L,2))
        ENDDO
      ENDDO
#ifndef __GFORTRAN__
!$OMP end do

! don't flip, input is the same direction as GFS  (bottom-up)
!$OMP do
#endif
      DO J=1,npts
        DO L=1,lev
           if(prsl(j,L) >= aerpres(j,1)) then
              DO ii=1, ntrcaer
               aerout(j,L,ii) = aerpm(j,1,ii)        !! sfc level
              ENDDO
           else if(prsl(j,L) <= aerpres(j,levsaer)) then
              DO ii=1, ntrcaer
               aerout(j,L,ii) = aerpm(j,levsaer,ii)  !! toa top
              ENDDO
           else
             DO  k=1, levsaer-1      !! from sfc to toa
              IF(prsl(j,L) < aerpres(j,k) .and. prsl(j,L)>aerpres(j,k+1)) then
                 i1 = k
                 i2 = min(k+1,levsaer)
                 exit
              ENDIF
             ENDDO
             tem  = 1.0 / (aerpres(j,i1) - aerpres(j,i2))
             tx1  = (prsl(j,L) - aerpres(j,i2)) * tem
             tx2  = (aerpres(j,i1) - prsl(j,L)) * tem
             DO ii = 1, ntrcaer
               aerout(j,L,ii) = aerpm(j,i1,ii)*tx1 + aerpm(j,i2,ii)*tx2
             ENDDO
           endif
        ENDDO   !L-loop
      ENDDO     !J-loop
#ifndef __GFORTRAN__
!$OMP end do

!$OMP end parallel
#endif

      RETURN
      END SUBROUTINE aerinterpol

      subroutine read_netfaer(nf, iflip,nt)
      use machine, only: kind_phys, kind_io4, kind_io8
      use aerclm_def
      use netcdf
      integer, intent(in) :: iflip, nf, nt
      integer      :: ncid, varid, i,j,k,ii,klev
      character    :: fname*50, mn*2, vname*10
      real(kind=kind_io4),allocatable,dimension(:,:,:) :: buff
      real(kind=kind_io4),allocatable,dimension(:,:,:,:):: buffx
      real(kind=kind_io4),allocatable,dimension(:,:)   :: pres_tmp
      
!! ===================================================================
      allocate (buff(lonsaer, latsaer, levsw))
      allocate (pres_tmp(lonsaer, levsw))
      allocate (buffx(lonsaer, latsaer, levsw, 1))

      write(mn,'(i2.2)') nf 
      fname=trim("aeroclim.m"//mn//".nc")
      call nf_open(fname , nf90_NOWRITE, ncid)

! ====> construct 3-d pressure array (Pa)
      call nf_inq_varid(ncid, "DELP", varid)
      call nf_get_var(ncid, varid, buff)

      do j = jamin, jamax
        do i = iamin, iamax
! constract pres_tmp (top-down), note input is top-down
          pres_tmp(i,1) = 0.
          do k=2, levsw
            pres_tmp(i,k) = pres_tmp(i,k-1)+buff(i,j,k)
          enddo    !k-loop
        enddo     !i-loop (lon)

! extract pres_tmp to fill aer_pres (in  Pa)
        do k = 1, levsaer
          if ( iflip == 0 )  then             ! data from toa to sfc
            klev = k
          else                                ! data from sfc to top
            klev = ( levsw - k ) + 1
          endif
          do i = iamin, iamax
            aer_pres(i,j,k,nt)    = 1.d0*pres_tmp(i,klev)
          enddo     !i-loop (lon)
        enddo     !k-loop (lev)
      enddo     !j-loop (lat)

! ====> construct 4-d aerosol array (kg/kg)
! merra2 data is top down
! for GFS, iflip 0: toa to sfc; 1: sfc to toa
      DO ii = 1, ntrcaerm
        vname=trim(specname(ii))
        call nf_inq_varid(ncid, vname, varid)
        call nf_get_var(ncid, varid, buffx)

        do j = jamin, jamax
          do k = 1, levsaer
! input is from toa to sfc
            if ( iflip == 0 )  then             ! data from toa to sfc
              klev = k
            else                                ! data from sfc to top
              klev = ( levsw - k ) + 1
            endif
            do i = iamin, iamax
              aerin(i,j,k,ii,nt) = 1.d0*buffx(i,j,klev,1)
              if(aerin(i,j,k,ii,nt) < 0 .or. aerin(i,j,k,ii,nt) > 1.)  then
                aerin(i,j,k,ii,nt) = 1.e-15
              endif
            enddo   !i-loop (lon)
          enddo     !k-loop (lev)
        enddo       !j-loop (lat)

      ENDDO         ! ii-loop (ntracaerm)

! close the file
      call nf_close(ncid)
      deallocate (buff, pres_tmp)
      deallocate (buffx)
      return
      END SUBROUTINE read_netfaer

end module aerinterp

